package twilight.companion.event;

import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.living.LivingAttackEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.ForgeRegistries;
import twilight.companion.TwilightCompanion;
import twilight.companion.entity.MiniYetiEntity;
import twilight.companion.entity.SummonSkullEntity;
import twilight.companion.entity.RotatingSnowBlockEntity;
import twilight.companion.init.ModEntities;
import twilightforest.init.TFParticleType;
import twilightforest.init.TFSounds;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

@Mod.EventBusSubscriber(modid = TwilightCompanion.MOD_ID)
public class MiniYetiSummonHandler {
    private static class SummonRitual {
        final BlockPos pos;
        final UUID playerUUID;
        final Level level;
        int ticksRemaining;
        int phase;
        SummonRitual(BlockPos pos, UUID playerUUID, Level level) {
            this.pos = pos;
            this.playerUUID = playerUUID;
            this.level = level;
            this.ticksRemaining = 120;
            this.phase = 0;
        }
    }

    private static final Map<BlockPos, SummonRitual> activeRituals = new HashMap<>();

    @SubscribeEvent
    public static void onRightClickBlock(PlayerInteractEvent.RightClickBlock event) {
        Level level = event.getLevel();
        Player player = event.getEntity();
        BlockPos pos = event.getPos();
        ItemStack itemStack = event.getItemStack();

        if (level.isClientSide()) return;

        if (!itemStack.is(ForgeRegistries.ITEMS.getValue(
            net.minecraft.resources.ResourceLocation.tryParse("twilightforest:alpha_yeti_trophy")))) {
            return;
        }

        if (checkSummonStructure(level, pos)) {
            if (activeRituals.containsKey(pos)) {
                return;
            }

            startSummonRitual(level, pos, player, itemStack);
            event.setCanceled(true);
        }
    }

    private static void startSummonRitual(Level level, BlockPos pos, Player player, ItemStack itemStack) {
        if (!player.isCreative()) {
            itemStack.shrink(1);
        }

        SummonSkullEntity skullEntity = new SummonSkullEntity(ModEntities.SUMMON_SKULL.get(), level, pos);
        skullEntity.setOwnerUUID(player.getUUID());
        skullEntity.setSourcePlayer(player);
        skullEntity.setCreativeMode(player.isCreative());
        level.addFreshEntity(skullEntity);

        if (level instanceof ServerLevel serverLevel) {
            createRotatingSnowBlocks(serverLevel, pos);
        }

        SummonRitual ritual = new SummonRitual(pos, player.getUUID(), level);
        activeRituals.put(pos, ritual);

        level.playSound(null, pos, TFSounds.ALPHA_YETI_GROWL.get(), SoundSource.BLOCKS, 1.0F, 0.5F);

        if (level instanceof ServerLevel serverLevel) {
            spawnInitialParticles(serverLevel, pos);
        }
    }

    @SubscribeEvent
    public static void onServerTick(TickEvent.ServerTickEvent event) {
        if (event.phase != TickEvent.Phase.END) return;

        Iterator<Map.Entry<BlockPos, SummonRitual>> iterator = activeRituals.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<BlockPos, SummonRitual> entry = iterator.next();
            SummonRitual ritual = entry.getValue();
            if (updateRitual(ritual)) {
                iterator.remove();
            }
        }
    }

    private static boolean updateRitual(SummonRitual ritual) {
        ritual.ticksRemaining--;

        if (!(ritual.level instanceof ServerLevel serverLevel)) {
            return true;
        }

        int totalTicks = 120;
        int elapsed = totalTicks - ritual.ticksRemaining;

        if (elapsed < 40) {
            ritual.phase = 0;
        } else if (elapsed < 80) {
            ritual.phase = 1;
        } else if (elapsed < 110) {
            ritual.phase = 2;
        } else {
            ritual.phase = 3;
        }

        switch (ritual.phase) {
            case 0 -> spawnPreparationEffects(serverLevel, ritual);
            case 1 -> spawnChargingEffects(serverLevel, ritual);
            case 2 -> spawnClimaxEffects(serverLevel, ritual);
            case 3 -> spawnCompletionEffects(serverLevel, ritual);
        }

        if (ritual.ticksRemaining <= 0) {
            completeSummonRitual(ritual);
            return true;
        }
        return false;
    }

    private static void completeSummonRitual(SummonRitual ritual) {
        if (!(ritual.level instanceof ServerLevel serverLevel)) return;
        spawnFinalSummonEffects(serverLevel, ritual.pos);
    }

    private static void createRotatingSnowBlocks(ServerLevel level, BlockPos centerPos) {
        createRotatingSnowBlock(level, centerPos, centerPos, 0);
        createRotatingSnowBlock(level, centerPos, centerPos.below(), 1);

        boolean eastWestT = 
        level.getBlockState(centerPos.west()).is(Blocks.SNOW_BLOCK) &&
        level.getBlockState(centerPos.east()).is(Blocks.SNOW_BLOCK);

        if (eastWestT) {
            createRotatingSnowBlock(level, centerPos, centerPos.west(), 2);
            createRotatingSnowBlock(level, centerPos, centerPos.east(), 3);
        } else {
            createRotatingSnowBlock(level, centerPos, centerPos.north(), 2);
            createRotatingSnowBlock(level, centerPos, centerPos.south(), 3);
        }
        removeSummonStructure(level, centerPos);
    }

    private static void createRotatingSnowBlock(ServerLevel level, BlockPos centerPos, BlockPos blockPos, int index) {
        RotatingSnowBlockEntity snowBlockEntity = new RotatingSnowBlockEntity(
        ModEntities.ROTATING_SNOW_BLOCK.get(), level, centerPos, blockPos, index);
        level.addFreshEntity(snowBlockEntity);
    }

    private static boolean checkSummonStructure(Level level, BlockPos centerPos) {
        if (!level.getBlockState(centerPos).is(Blocks.SNOW_BLOCK)) {
            return false;
        }

        if (!level.getBlockState(centerPos.below()).is(Blocks.SNOW_BLOCK)) {
            return false;
        }

        boolean eastWestT = level.getBlockState(centerPos.west()).is(Blocks.SNOW_BLOCK) &&
        level.getBlockState(centerPos.east()).is(Blocks.SNOW_BLOCK);

        boolean northSouthT = level.getBlockState(centerPos.north()).is(Blocks.SNOW_BLOCK) &&
        level.getBlockState(centerPos.south()).is(Blocks.SNOW_BLOCK);

        return eastWestT || northSouthT;
    }

    private static void removeSummonStructure(Level level, BlockPos centerPos) {
        level.removeBlock(centerPos, false);
        level.removeBlock(centerPos.below(), false);

        boolean eastWestT = 
        level.getBlockState(centerPos.west()).is(Blocks.SNOW_BLOCK) &&
        level.getBlockState(centerPos.east()).is(Blocks.SNOW_BLOCK);

        if (eastWestT) {
            level.removeBlock(centerPos.west(), false);
            level.removeBlock(centerPos.east(), false);
        } else {
            level.removeBlock(centerPos.north(), false);
            level.removeBlock(centerPos.south(), false);
        }
    }

    private static void spawnInitialParticles(ServerLevel level, BlockPos pos) {
        for (int i = 0; i < 20; i++) {
            double angle = (i / 20.0) * 2 * Math.PI;
            double x = pos.getX() + 0.5 + Math.cos(angle) * 2.0;
            double z = pos.getZ() + 0.5 + Math.sin(angle) * 2.0;
            double y = pos.getY() + 1.0;

            level.sendParticles(ParticleTypes.SNOWFLAKE, x, y, z, 1, 0, 0.1, 0, 0.02);
        }
    }

    private static void spawnPreparationEffects(ServerLevel level, SummonRitual ritual) {
        BlockPos pos = ritual.pos;
        int elapsed = 120 - ritual.ticksRemaining;

        if (elapsed % 4 == 0) {
            double angle = (elapsed * 0.1) % (2 * Math.PI);
            for (int i = 0; i < 3; i++) {
                double currentAngle = angle + (i * 2 * Math.PI / 3);
                double x = pos.getX() + 0.5 + Math.cos(currentAngle) * 1.5;
                double z = pos.getZ() + 0.5 + Math.sin(currentAngle) * 1.5;
                double y = pos.getY() + 0.5 + Math.sin(elapsed * 0.05) * 0.3;

                level.sendParticles(ParticleTypes.SNOWFLAKE, x, y, z, 1, 0, 0, 0, 0.01);
            }
        }

        if (elapsed % 20 == 0) {
            level.playSound(null, pos, SoundEvents.POWDER_SNOW_STEP, SoundSource.BLOCKS, 0.3F, 1.5F);
        }
    }

    private static void spawnChargingEffects(ServerLevel level, SummonRitual ritual) {
        BlockPos pos = ritual.pos;
        int elapsed = 120 - ritual.ticksRemaining;

        if (elapsed % 2 == 0) {
            double angle = (elapsed * 0.2) % (2 * Math.PI);
            for (int i = 0; i < 6; i++) {
                double currentAngle = angle + (i * Math.PI / 3);
                double radius = 1.0 + Math.sin(elapsed * 0.1) * 0.5;
                double x = pos.getX() + 0.5 + Math.cos(currentAngle) * radius;
                double z = pos.getZ() + 0.5 + Math.sin(currentAngle) * radius;
                double y = pos.getY() + 1.0 + Math.sin(elapsed * 0.08) * 0.5;

                level.sendParticles(ParticleTypes.SNOWFLAKE, x, y, z, 1, 0, 0.05, 0, 0.03);

                if (i % 2 == 0) {
                    level.sendParticles(TFParticleType.SNOW.get(), x, y, z, 1, 0, 0, 0, 0.02);
                }
            }
        }

        if (elapsed % 15 == 0) {
            level.playSound(null, pos, SoundEvents.AMETHYST_BLOCK_CHIME, SoundSource.BLOCKS, 0.5F, 0.8F);
        }
    }

    private static void spawnClimaxEffects(ServerLevel level, SummonRitual ritual) {
        BlockPos pos = ritual.pos;
        int elapsed = 120 - ritual.ticksRemaining;

        double angle = (elapsed * 0.3) % (2 * Math.PI);
        for (int i = 0; i < 12; i++) {
            double currentAngle = angle + (i * Math.PI / 6);
            double radius = 0.8 + Math.sin(elapsed * 0.15) * 0.3;
            double x = pos.getX() + 0.5 + Math.cos(currentAngle) * radius;
            double z = pos.getZ() + 0.5 + Math.sin(currentAngle) * radius;
            double y = pos.getY() + 1.5 + Math.sin(elapsed * 0.12) * 0.8;

            level.sendParticles(ParticleTypes.SNOWFLAKE, x, y, z, 2, 0.1, 0.1, 0.1, 0.05);
            level.sendParticles(TFParticleType.SNOW.get(), x, y, z, 1, 0, 0, 0, 0.03);
        }

        for (int i = 0; i < 5; i++) {
            double x = pos.getX() + 0.5 + (level.random.nextDouble() - 0.5) * 0.5;
            double z = pos.getZ() + 0.5 + (level.random.nextDouble() - 0.5) * 0.5;
            double y = pos.getY() + level.random.nextDouble() * 3.0;

            level.sendParticles(ParticleTypes.CLOUD, x, y, z, 1, 0, 0.2, 0, 0.1);
        }

        if (elapsed % 10 == 0) {
            level.playSound(null, pos, TFSounds.ALPHA_YETI_ALERT.get(), SoundSource.BLOCKS, 0.8F, 1.2F);
        }
    }

    private static void spawnCompletionEffects(ServerLevel level, SummonRitual ritual) {
        BlockPos pos = ritual.pos;

        for (int i = 0; i < 8; i++) {
            double angle = (i / 8.0) * 2 * Math.PI;
            double x = pos.getX() + 0.5 + Math.cos(angle) * 0.3;
            double z = pos.getZ() + 0.5 + Math.sin(angle) * 0.3;
            double y = pos.getY() + 1.0;

            level.sendParticles(ParticleTypes.SNOWFLAKE, x, y, z, 3, 0, 0.3, 0, 0.1);
            level.sendParticles(TFParticleType.SNOW.get(), x, y, z, 2, 0, 0.2, 0, 0.08);
        }
    }

    private static void spawnFinalSummonEffects(ServerLevel level, BlockPos pos) {
        for (int i = 0; i < 50; i++) {
            double angle = level.random.nextDouble() * 2 * Math.PI;
            double radius = level.random.nextDouble() * 3.0;
            double x = pos.getX() + 0.5 + Math.cos(angle) * radius;
            double z = pos.getZ() + 0.5 + Math.sin(angle) * radius;
            double y = pos.getY() + 1.0 + level.random.nextDouble() * 2.0;

            level.sendParticles(ParticleTypes.SNOWFLAKE, x, y, z, 1,
            (level.random.nextDouble() - 0.5) * 0.5,
            level.random.nextDouble() * 0.3,
            (level.random.nextDouble() - 0.5) * 0.5,
            0.1);
        }

        for (int i = 0; i < 30; i++) {
            double x = pos.getX() + 0.5 + (level.random.nextDouble() - 0.5) * 4.0;
            double z = pos.getZ() + 0.5 + (level.random.nextDouble() - 0.5) * 4.0;
            double y = pos.getY() + 1.0 + level.random.nextDouble() * 1.5;

            level.sendParticles(TFParticleType.SNOW.get(), x, y, z, 1, 0, 0.1, 0, 0.05);
        }

        for (int i = 0; i < 20; i++) {
            double x = pos.getX() + 0.5 + (level.random.nextDouble() - 0.5) * 2.0;
            double z = pos.getZ() + 0.5 + (level.random.nextDouble() - 0.5) * 2.0;
            double y = pos.getY() + 0.5 + level.random.nextDouble() * 1.0;

            level.sendParticles(ParticleTypes.CLOUD, x, y, z, 1, 0, 0.05, 0, 0.02);
        }
        level.playSound(null, pos, SoundEvents.GENERIC_EXPLODE, SoundSource.BLOCKS, 0.5F, 1.5F);
    }

    @SubscribeEvent
    public static void onLivingAttack(LivingAttackEvent event) {
        if (event.getEntity() instanceof MiniYetiEntity miniYeti &&
            event.getSource().getEntity() instanceof Player player) {
            if (miniYeti.isTame() && miniYeti.isOwnedBy(player)) {
                event.setCanceled(true);
            }
        }
    }
}